using System;
using System.Runtime.Serialization;
using NodaTime.Text;

namespace NodaTime.Serialization.ServiceStackText
{
    public class StandardServiceStackSerializer<T> : IServiceStackSerializer<T>
    {
        private IPattern<T> pattern;

        private Func<string, T> serviceStackFallbackDeSerializer;

        private Action<T> serializationValidator;

        public bool UseRawSerializer
        {
            get { return false; }
        }

        public StandardServiceStackSerializer(
            IPattern<T> pattern,
            Func<string, T> serviceStackFallbackDeSerializer = null,
            Action<T> serializationValidator = null)
        {
            if (pattern == null)
            {
                throw new ArgumentNullException("pattern");
            }
            this.pattern = pattern;
            this.serializationValidator = serializationValidator;
            this.serviceStackFallbackDeSerializer = serviceStackFallbackDeSerializer;
        }

        public string Serialize(T value)
        {
            if (this.serializationValidator != null)
            {
                this.serializationValidator(value);
            }
            
            return pattern.Format(value);
        }

        public T Deserialize(string text)
        {
            var parsedResult = pattern.Parse(text);

            if (parsedResult.Success)
            {
                return parsedResult.Value;
            }

            if (serviceStackFallbackDeSerializer == null)
            {
                throw parsedResult.Exception;
            }

            T fallbackObj;

            try
            {
                fallbackObj = serviceStackFallbackDeSerializer(text);
            }
            catch (SerializationException)
            {
                //If fallback fails, throw original exception.
                throw parsedResult.Exception;
            }

            return fallbackObj;
        }
    }
}